<?php
declare (strict_types=1);

namespace app;

use Curl\Curl;
use Luler\Helpers\CommonHelper;
use Luler\Helpers\MultiProcessHelper;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;

class Debug extends Command
{
    public $video_url;

    protected function configure()
    {
        // 指令配置
        $this->setName('debug')
            ->addArgument('video_url', Argument::OPTIONAL, '视频链接')
            ->setDescription('调试专用命令');

    }

    protected function execute(Input $input, Output $output)
    {
        try {
            $video_url = $input->getArgument('video_url');
            if (is_null($video_url)) {
                exit("视频链接不能为空" . PHP_EOL);
            }
            if (!filter_var($video_url, FILTER_VALIDATE_URL)) {
                exit("视频链接不是格式有误" . PHP_EOL);
            }
            $this->video_url = $video_url;
            //如果是网页链接，则充其量解析一下
            $headers = $this->getUrlHeaders($this->video_url);
            if (isset($headers['content-type']) && stripos($headers['content-type'], 'text/html') !== false) {
                $content = file_get_contents($this->video_url, false, $this->getUrlContext());
                preg_match_all('/[\'\"]{1}([^\'\"]*(\.m3u8|\.mp4)[^\'\"]*)[\'\"]{1}/i', $content, $match);
                $video_urls = $match[1];
                $video_urls = array_unique($video_urls);
                $useful_video_urls = [];
                foreach ($video_urls as $url) {
                    if (filter_var($url, FILTER_VALIDATE_URL)) {
                        $headers = $this->getUrlHeaders($url);
                        $length = (int)($headers['content-length'] ?? 0);
                        $useful_video_urls[] = [
                            'url' => $url,
                            'size' => CommonHelper::convertSize($length),
                        ];
                    }
                }
                if (empty($useful_video_urls)) {
                    exit('该网页下找到0个视频链接' . PHP_EOL);
                }
                $video_count = count($useful_video_urls);
                $output->writeln('该网页下找到' . $video_count . '个视频链接:');
                foreach ($useful_video_urls as $k => $value) {
                    $output->writeln('[' . ($k + 1) . ']:' . $value['url'] . '(' . $value['size'] . ')');
                }
                $ranges = range(1, $video_count);
                while (true) {
                    $index = $output->ask($input, '请输入需要下载的项目序号[' . join(',', $ranges) . ']');
                    if (is_numeric($index) && in_array($index, $ranges)) {
                        break;
                    }
                }
                $video_url = $useful_video_urls[$index - 1]['url'];
            }

            if (stripos($video_url, '.m3u8')) {
                $this->downLoadM3u8($video_url);
            } elseif (stripos($video_url, '.mp4')) {
                $this->downLoadVideo($video_url);
            } else {
                exit('视频链接无效' . PHP_EOL);
            }
        } catch (\Exception $e) {
            echo $e->getTraceAsString() . PHP_EOL;
            echo $e->getMessage() . PHP_EOL;
        }

    }

    /**
     * 下载一般视频文件
     * @param $video_url
     * @author 我只想看看蓝天 <1207032539@qq.com>
     */
    function downLoadVideo($video_url)
    {
        $headers = $this->getUrlHeaders($video_url);
        $file_size = (int)($headers['content-length'] ?? 0);
        if (!isset($headers['content-type']) && stripos($headers['content-type'], 'video') === false) {
            exit('视频文件无效' . PHP_EOL);
        }
        $context = $this->getUrlContext($video_url);
        $fd = fopen($video_url, 'rb', false, $context);
        $read_buffer = 4096;
        $count = ceil($file_size / $read_buffer);
        $value = basename(parse_url($video_url)['path']);
        $extend = strstr($value, '.');
        $filename = $this->getVideoBasePath() . date('Y-m-d-H-i-s.') . md5($video_url) . $extend;
        $index = 1;
        while (!feof($fd)) {
            if ($index <= $count) {
                $this->cmd_progress_bar($count, $index, '视频文件下载开始', '视频文件下载完成');
                $index++;
            }
            $res = fread($fd, $read_buffer);
            file_put_contents($filename, $res, FILE_APPEND);
        }
        fclose($fd);

        echo PHP_EOL . '视频文件保存路径:' . $filename . PHP_EOL;
    }

    /**
     * 下载m3u8视频文件
     * @param $m3u8_url
     * @author 我只想看看蓝天 <1207032539@qq.com>
     */
    function downLoadM3u8($m3u8_url)
    {
        $ds = DIRECTORY_SEPARATOR;
        $curl = new Curl();
        $curl->setHeader('User-Agent', config('app.m3u8.header.user-agent'));
        $curl->setHeader('Accept-Encoding', 'gzip, deflate, br');
        $curl->setOpt(CURLOPT_ENCODING, 'gzip');
        $curl->get($m3u8_url);
        if ($curl->getHttpStatus() != 200) {
            $content = file_get_contents($m3u8_url, false, $this->getUrlContext());
        } else {
            $content = $curl->response;
        }
        $curl->close();
        $content = explode("\n", $content);
        $ts_all = [];
        $key_url = '';
        $key = '';
        $base_url = dirname($m3u8_url);
        foreach ($content as $k => $item) {
            if (stripos($item, 'EXT-X-KEY')) {
                preg_match("/URI=[\"\']?([^\"\'\,]*)[\"\']?/i", $item, $match);
                $key_url = $match[1] ?? '';
                if (!empty($key_url)) {
                    if (!filter_var($key_url, FILTER_VALIDATE_URL)) {
                        $key_url = $base_url . '/' . $key_url;
                    }
                }
            }
            if (stripos($item, 'EXTINF')) {
                $ts_all[] = $content[$k + 1];
            }
        }
        if (empty($ts_all)) {
            exit("获取到0个视频片段,请检查链接是否有效" . PHP_EOL);
        }
        //获取解密秘钥
        if (!empty($key_url)) {
            $key = file_get_contents($key_url, false, $this->getUrlContext());
            if (empty($key)) {
                exit("获取秘钥失败，该视频可能存在定制化加密算法" . PHP_EOL);
            }
        }
        $md5 = md5(stristr($m3u8_url, '.m3u8', true));
        $path = $this->getVideoBasePath() . date('Y-m-d-H-i-s.') . $md5 . $ds;
        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }
        //下载ts视频分片
        $ts_count = count($ts_all);
        foreach ($ts_all as $k => $value) {
            $this->cmd_progress_bar($ts_count, ($k + 1), '正在下载视频片段', '下载视频片段完成');
            $download_function = function () use ($k, $ts_count, $value, $base_url, $path, $key) {
                if (filter_var($value, FILTER_VALIDATE_URL)) {
                    $ts_url = $value;
                } else {
                    $ts_url = $base_url . '/' . $value;
                }
                $value = basename(parse_url($value)['path']);
                $raw_content = file_get_contents($ts_url, false, $this->getUrlContext());
                //内容需要解密
                if (!empty($key)) {
                    $raw_content = openssl_decrypt($raw_content, 'aes-128-cbc', $key, 1);
                    if (empty($raw_content)) {
                        exit("解密失败，该视频可能存在定制化加密算法" . PHP_EOL);
                    }
                }
                file_put_contents($path . $value, $raw_content);
            };
            if (function_exists('pcntl_fork')) {
                MultiProcessHelper::instance()->multiProcessTask($download_function);
            } else {
                $download_function();
            }
        }
        MultiProcessHelper::recycleProcess();
        //合并ts视频
        $extend = '';
        $result_file_path = '';
        foreach ($ts_all as $k => $value) {
            $this->cmd_progress_bar($ts_count, ($k + 1), '正在合并视频片段', '合并视频片段完成');

            $value = basename(parse_url($value)['path']);
            if (empty($extend)) {
                $extend = strstr($value, '.');
            }
            $result_file_path = rtrim($path, $ds) . $extend;
            if (file_exists($path . $value)) {
                file_put_contents(
                    $result_file_path,
                    file_get_contents($path . $value, false, $this->getUrlContext()),
                    FILE_APPEND);
                unlink($path . $value);
            }
        }
        rmdir($path);
        echo PHP_EOL . '视频文件保存路径:' . $result_file_path . PHP_EOL;
    }

    /**
     * @param $url
     * @return array
     * @author 我只想看看蓝天 <1207032539@qq.com>
     */
    public function getUrlHeaders($url)
    {
        $context = $this->getUrlContext($url);
        $headers = get_headers($url, 0, $context);
        $res = [];
        foreach ($headers as $header) {
            $temp = explode(':', $header);
            if (count($temp) == 2) {
                $res[strtolower($temp[0])] = trim($temp[1]);
            }
        }
        return $res;
    }

    /**
     * 构造链接上下文
     * @param $url
     * @author 我只想看看蓝天 <1207032539@qq.com>
     */
    public function getUrlContext($url = null)
    {
        if (is_null($url)) {
            $url = $this->video_url;
        }
        $parse_url = parse_url($url);
        $referer = $parse_url['scheme'] . '://' . $parse_url['host'];
        $header = [];
        $header[] = "Referer:{$referer}";
        if (!empty(config('app.m3u8.header.cookie'))) {
            $header[] = "Cookie:" . config('app.m3u8.header.cookie');
        }
        if (!empty(config('app.m3u8.header.user-agent'))) {
            $header[] = "User-Agent:" . config('app.m3u8.header.user-agent');
        }
        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => join("\r\n", $header),
            ],
            'ssl' => [
                'verify_host' => false,
                'verify_peer' => false,
                'verify_peer_name' => false,
            ],
        ]);
        return $context;
    }

    public function getVideoBasePath()
    {
        $path = runtime_path('video');
        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }
        return $path;
    }

    public function cmd_progress_bar($total, $current, $begin_str = '开始', $finished_str = '结束', $begin_num = 1, $screen_width = 50)
    {
        if ($begin_str && $current == $begin_num) {
            echo date('Y-m-d H:i:s') . ':' . $begin_str . PHP_EOL;
        }
        printf("  进度: [%-" . $screen_width . "s] %d%% \r", str_repeat('=', (int)floor(($current / $total) * $screen_width)) . '>', $current / $total * 100);
        if ($finished_str && $total == $current) {
            echo PHP_EOL . date('Y-m-d H:i:s') . ':' . $finished_str . PHP_EOL;
        }
    }
}
